#ifndef Vectors_h__
#define Vectors_h__

#include <cmath>

namespace math
{
class Vector2
{
public:
	float x, y;
	
public:
	Vector2() :x(0.f), y(0.f){ }

	Vector2(const Vector2& v): x(v.x), y (v.y) {}
	Vector2(float fx, float fy) : x(fx), y(fy) {}

	Vector2& operator=(const Vector2& v) {
		x = v.x; y = v.y;
		return *this;
	}

	bool operator==(const Vector2& v) const {
		return x == v.x && y == v.y;
	}

	bool operator!=(const Vector2& v) const {
		return x != v.x || y != v.y;
	}

	Vector2 operator - () const {
		return Vector2(-x, -y);
	}

	Vector2 operator+ (const Vector2& v) const {
		return Vector2(x + v.x, y + v.y);
	}

	Vector2 operator- (const Vector2& v) const {
		return Vector2(x - v.x, y - v.y);
	}

	Vector2 operator* (float s) const {
		return Vector2(x * s, y * s);
	}

	Vector2 operator/ (float s) const {
		float fInv = 1 / s;
		return Vector2(x * fInv, y * fInv);
	}

	Vector2& operator+= (const Vector2& v) {
		x += v.x; y += v.y; 
		return *this;
	}

	Vector2& operator-= (const Vector2& v) {
		x -= v.x; y -= v.y;
		return *this;
	}

	Vector2& operator*= (float s) {
		x *= s; y *= s;
		return *this;
	}

	Vector2& operator/= (float s) {
		float fInv = 1 / s;
		x = fInv; y *= fInv;
		return *this;
	}

	void Zero() {
		x = y = 0.0f;
	}

	float SquareLength() const {
		return x * x + y * y ;
	}

	float Length() const {
		return sqrtf(SquareLength());
	}

	void Normalize() {
		float fLength = Length();
		if (fLength > 0.0f)
			*this /= fLength;
	}

	float operator* (const Vector2& v) {
		return x * v.x + y * v.y;
	}
};
class Vector3
{
public:
	union
	{
		struct 
		{
			float x, y, z;
		};
		struct
		{
			float r, g, b;
		};
	};	
public:
	Vector3() :x(0.f), y(0.f), z(0.f) { }

	Vector3(const Vector3& v): x(v.x), y (v.y), z(v.z) {}
	Vector3(float fx, float fy, float fz) : x(fx), y(fy), z(fz) {}

	Vector3& operator=(const Vector3& v) {
		x = v.x; y = v.y; z = v.z;
		return *this;
	}

	bool operator==(const Vector3& v) const {
		return x == v.x && y == v.y && z == v.z;
	}

	bool operator!=(const Vector3& v) const {
		return x != v.x || y != v.y || z != v.z;
	}

	Vector3 operator - () const {
		return Vector3(-x, -y, -z);
	}

	Vector3 operator+ (const Vector3& v) const {
		return Vector3(x + v.x, y + v.y, z + v.z);
	}

	Vector3 operator- (const Vector3& v) const {
		return Vector3(x - v.x, y - v.y, z - v.z);
	}

	Vector3 operator* (float s) const {
		return Vector3(x * s, y * s, z * s);
	}

	Vector3 operator/ (float s) const {
		float fInv = 1 / s;
		return Vector3(x * fInv, y * fInv, z * fInv);
	}

	Vector3& operator+= (const Vector3& v) {
		x += v.x; y += v.y; z += v.z;
		return *this;
	}

	Vector3& operator-= (const Vector3& v) {
		x -= v.x; y -= v.y; z -= v.z;
		return *this;
	}

	Vector3& operator*= (float s) {
		x *= s; y *= s; z *= s;
		return *this;
	}

	Vector3& operator/= (float s) {
		float fInv = 1 / s;
		x = fInv; y *= fInv; z *= fInv;
		return *this;
	}

	void Zero() {
		x = y = z = 0.0f;
	}

	float SquareLength() const {
		return x * x + y * y + z * z;
	}

	float Length() const {
		return sqrtf(SquareLength());
	}

	void Normalize() {
		float fLength = Length();
		if (fLength > 0.0f)
			*this /= fLength;
	}

	float operator* (const Vector3& v) {
		return x * v.x + y * v.y + z * v.z;
	}
};
class Vector4
{
public:
	union
	{
		struct 
		{
			float x, y, z, w;
		};
		struct
		{
			float r, g, b, a;
		};
	};	
public:
	Vector4() :x(0.f), y(0.f), z(0.f), w(0.f) { }

	Vector4(const Vector4& v): x(v.x), y (v.y), z(v.z), w(v.w) {}
	Vector4(float fx, float fy, float fz, float fw) : x(fx), y(fy), z(fz), w(fw) {}

	Vector4& operator=(const Vector4& v) {
		x = v.x; y = v.y; z = v.z; w = v.w;
		return *this;
	}

	bool operator==(const Vector4& v) const {
		return x == v.x && y == v.y && z == v.z && w == v.w;
	}

	bool operator!=(const Vector4& v) const {
		return x != v.x || y != v.y || z != v.z || w != v.w;
	}

	Vector4 operator - () const {
		return Vector4(-x, -y, -z, -w);
	}

	Vector4 operator+ (const Vector4& v) const {
		return Vector4(x + v.x, y + v.y, z + v.z, w + v.w);
	}

	Vector4 operator- (const Vector4& v) const {
		return Vector4(x - v.x, y - v.y, z - v.z, w + v.w);
	}

	Vector4 operator* (float s) const {
		return Vector4(x * s, y * s, z * s, w * s);
	}

	Vector4 operator/ (float s) const {
		float fInv = 1 / s;
		return Vector4(x * fInv, y * fInv, z * fInv, w * fInv);
	}

	Vector4& operator+= (const Vector4& v) {
		x += v.x; y += v.y; z += v.z; w += v.w;
		return *this;
	}

	Vector4& operator-= (const Vector4& v) {
		x -= v.x; y -= v.y; z -= v.z; w -= v.w;
		return *this;
	}

	Vector4& operator*= (float s) {
		x *= s; y *= s; z *= s; w *= s;
		return *this;
	}

	Vector4& operator/= (float s) {
		float fInv = 1 / s;
		x = fInv; y *= fInv; z *= fInv; w *= fInv;
		return *this;
	}

	void Zero() {
		x = y = z = 0.0f;
	}

	float SquareLength() const {
		return x * x + y * y + z * z + w * w;
	}

	float Length() const {
		return sqrtf(SquareLength());
	}

	void Normalize() {
		float fLength = Length();
		if (fLength > 0.0f)
			*this /= fLength;
	}

	float operator* (const Vector4& v) {
		return x * v.x + y * v.y + z * v.z + w * v.w;
	}
};

inline Vector3 CrossProduct(const Vector3& a, const Vector3& b)
{
	return Vector3 (
		a.y * b.z - a.z * b.y,
		a.z * b.x - a.x * b.z,
		a.x * b.y - a.y * b.x
	);
}

inline Vector3 operator*(float k, const Vector3& v)
{
	return Vector3(k * v.x, k * v.y, k * v.z);
}

inline float Distance(const Vector3& a, const Vector3& b)
{
	Vector3 tmp = a - b;
	return tmp.Length();
}


}

#endif // Vectors_h__
